using System;
using System.Collections;
using System.Diagnostics;
using System.Text;
using ActiproSoftware.SyntaxEditor.Addons.Simple.Ast;

namespace ActiproSoftware.SyntaxEditor.Addons.Simple.Ast {

	/// <summary>
	/// Represents a function declaration.
	/// </summary>
	public class FunctionDeclaration : ActiproSoftware.SyntaxEditor.Addons.Simple.Ast.AstNode, ICollapsibleNode {

		private int					blockEndOffset						= -1;
		private int					blockStartOffset					= -1;

		/// <summary>
		/// Gets the context ID for the block statement.
		/// </summary>
		public const byte BlockStatementContextID = ActiproSoftware.SyntaxEditor.Addons.Simple.Ast.AstNode.AstNodeContextIDBase;

		/// <summary>
		/// Gets the context ID for the name.
		/// </summary>
		public const byte NameContextID = ActiproSoftware.SyntaxEditor.Addons.Simple.Ast.AstNode.AstNodeContextIDBase + 1;

		/// <summary>
		/// Gets the context ID for the collection of parameters.
		/// </summary>
		public const byte ParameterContextID = ActiproSoftware.SyntaxEditor.Addons.Simple.Ast.AstNode.AstNodeContextIDBase + 2;

		/// <summary>
		/// Gets the minimum context ID that should be used in your code for AST nodes inheriting this class.
		/// </summary>
		/// <remarks>
		/// Base all your context ID constants off of this value.
		/// </remarks>
		protected const byte FunctionDeclarationContextIDBase = ActiproSoftware.SyntaxEditor.Addons.Simple.Ast.AstNode.AstNodeContextIDBase + 3;

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		// OBJECT
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Initializes a new instance of the <c>FunctionDeclaration</c> class. 
		/// </summary>
		public FunctionDeclaration() {}

		/// <summary>
		/// Initializes a new instance of the <c>FunctionDeclaration</c> class. 
		/// </summary>
		/// <param name="textRange">The <see cref="TextRange"/> of the AST node.</param>
		public FunctionDeclaration(TextRange textRange) : base(textRange) {}

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		// PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Accepts the specified visitor for visiting this node.
		/// </summary>
		/// <param name="visitor">The visitor to accept.</param>
		/// <remarks>This method is part of the visitor design pattern implementation.</remarks>
		protected override void AcceptCore(AstVisitor visitor) {
			if (visitor.OnVisiting(this)) {
				// Visit children
				if (this.ChildNodeCount > 0)
					this.AcceptChildren(visitor, this.ChildNodes);
			}
			visitor.OnVisited(this);
		}
		
		/// <summary>
		/// Gets the image index that is applicable for displaying this node in a user interface control.
		/// </summary>
		/// <value>The image index that is applicable for displaying this node in a user interface control.</value>
		public override int ImageIndex {
			get {
				return (System.Int32)ActiproSoftware.Products.SyntaxEditor.IconResource.PublicMethod;
			}
		}

		/// <summary>
		/// Gets the <see cref="SimpleNodeType"/> that identifies the type of node.
		/// </summary>
		/// <value>The <see cref="SimpleNodeType"/> that identifies the type of node.</value>
		public override SimpleNodeType NodeType { 
			get {
				return SimpleNodeType.FunctionDeclaration;
			}
		}

		/// <summary>
		/// Gets or sets the block statement.
		/// </summary>
		/// <value>The block statement.</value>
		public BlockStatement BlockStatement {
			get {
				return this.GetChildNode(FunctionDeclaration.BlockStatementContextID) as BlockStatement;
			}
			set {
				this.ChildNodes.Replace(value, FunctionDeclaration.BlockStatementContextID);
			}
		}

		/// <summary>
		/// Gets or sets the name.
		/// </summary>
		/// <value>The name.</value>
		public Identifier Name {
			get {
				return this.GetChildNode(FunctionDeclaration.NameContextID) as Identifier;
			}
			set {
				this.ChildNodes.Replace(value, FunctionDeclaration.NameContextID);
			}
		}

		/// <summary>
		/// Gets the collection of parameters.
		/// </summary>
		/// <value>The collection of statements.</value>
		public IAstNodeList Parameters {
			get {
				return new AstNodeListWrapper(this, FunctionDeclaration.ParameterContextID);
			}
		}

		/// <summary>
		/// Gets the offset at which the outlining node ends.
		/// </summary>
		/// <value>The offset at which the outlining node ends.</value>
		int ICollapsibleNode.EndOffset {
			get {
				return blockEndOffset;
			}
		}
		
		/// <summary>
		/// Gets whether the node is collapsible.
		/// </summary>
		/// <value>
		/// <c>true</c> if the node is collapsible; otherwise, <c>false</c>.
		/// </value>
		bool ICollapsibleNode.IsCollapsible {
			get {
				return (blockStartOffset != -1) && ((blockStartOffset < blockEndOffset) || (blockEndOffset == -1));
			}
		}
		
		/// <summary>
		/// Gets the offset at which the outlining node starts.
		/// </summary>
		/// <value>The offset at which the outlining node starts.</value>
		int ICollapsibleNode.StartOffset {
			get {
				return blockStartOffset;
			}
		}
		
		/// <summary>
		/// Gets whether the outlining indicator should be visible for the node.
		/// </summary>
		/// <value>
		/// <c>true</c> if the outlining indicator should be visible for the node; otherwise, <c>false</c>.
		/// </value>
		bool IOutliningNodeParseData.IndicatorVisible {
			get {
				return true;
			}
		}
		
		/// <summary>
		/// Gets whether the outlining node is for a language transition.
		/// </summary>
		/// <value>
		/// <c>true</c> if the outlining node is for a language transition; otherwise, <c>false</c>.
		/// </value>
		bool IOutliningNodeParseData.IsLanguageTransition {
			get {
				return false;
			}
		}
		
		/// <summary>
		/// Gets or sets the end character offset of the end block delimiter in the original source code that generated the AST node.
		/// </summary>
		/// <value>The end character offset of the end block delimiter in the original source code that generated the AST node.</value>
		/// <remarks>
		/// This value may be <c>-1</c> if there is no source code information for the end character offset.
		/// </remarks>
		public int BlockEndOffset {
			get {
				return blockEndOffset;
			}
			set {
				blockEndOffset = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the start character offset of the start block delimiter in the original source code that generated the AST node.
		/// </summary>
		/// <value>The start character offset of the start block delimiter in the original source code that generated the AST node.</value>
		/// <remarks>
		/// This value may be <c>-1</c> if there is no source code information for the start character offset.
		/// </remarks>
		public int BlockStartOffset {
			get {
				return blockStartOffset;
			}
			set {
				blockStartOffset = value;
			}
		}
		
		/// <summary>
		/// Gets text representing the node that can be used for display, such as in a document outline.
		/// </summary>
		/// <value>Text representing the node that can be used for display, such as in a document outline.</value>
		public override string DisplayText {
			get {
				StringBuilder text = new StringBuilder();
				text.Append("Function ");
				text.Append(this.Name.Text);
				text.Append("(");
				IAstNodeList parameters = this.Parameters;
				for (int index = 0; index < parameters.Count; index++) {
					if (index > 0)
						text.Append(", " );
					text.Append(((Identifier)parameters[index]).Text);
				}
				text.Append(")");
				return text.ToString();
			}
		}
		
		/// <summary>
		/// Gets the character offset at which to navigate when the editor's caret should jump to the text representation of the AST node.
		/// </summary>
		/// <value>The character offset at which to navigate when the editor's caret should jump to the text representation of the AST node.</value>
		public override int NavigationOffset {
			get {
				Identifier name = this.Name;
				if ((name != null) && (name.HasStartOffset))
					return name.NavigationOffset;
				else
					return base.NavigationOffset;
			}
		}

	}

}
